<!DOCTYPE html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="UTF-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module">
      import { setStrictTemplatePolicy } from '../../lib/utils/settings.js';
      import { Debouncer } from '../../lib/utils/debounce.js';
      setStrictTemplatePolicy(true);
      // Errors thrown in Polymer's debouncer queue get re-thrown
      // via setTimeout, making them particulary difficult to verify;
      // Wrap debouncer callbacks and store on the globalError to test later
      const debounce = Debouncer.debounce;
      Debouncer.debounce = function(debouncer, asyncModule, callback) {
        return debounce(debouncer, asyncModule, function() {
          try { 
            callback(); 
          } catch(error) {
            if (!window.uncaughtErrorFilter || !window.uncaughtErrorFilter(error)) {
              throw error;
            }
          }
        });
      };
    </script>
</head>
<body>

  <dom-module id="no-dm">
    <template>Should never be used</template>
    <script type="module">
      import {PolymerElement} from '../../polymer-element.js';
      class TrustedElement extends PolymerElement {
        static get is() { return 'no-dm'; }
      }
      customElements.define(TrustedElement.is, TrustedElement);
    </script>
  </dom-module>

  <dom-module id="no-dm-legacy">
    <template>Should never be used</template>
    <script type="module">
      import {Polymer} from '../../polymer-legacy.js';
      Polymer({is: 'no-dm-legacy'});
    </script>
  </dom-module>

  <dom-module id="trusted-element">
    <template>Trusted</template>
    <script type="module">
      import {PolymerElement} from '../../polymer-element.js';
      class TrustedElement extends PolymerElement {
        static get is() { return 'trusted-element'; }
      }
      customElements.define(TrustedElement.is, TrustedElement);
    </script>
  </dom-module>
    
  <dom-module id="trusted-element-legacy">
    <template>Trusted</template>
    <script type="module">
      import {Polymer} from '../../polymer-legacy.js';
      Polymer({is: 'trusted-element-legacy'});
    </script>
  </dom-module>
  
  <div id="target"></div>
    
  <script type="module">
    import {PolymerElement, html} from '../../polymer-element.js';
    import {Polymer} from '../../polymer-legacy.js';
    import {flush} from '../../lib/utils/flush.js';
    import {setAllowTemplateFromDomModule} from '../../lib/utils/settings.js';

    suite('strictTemplatePolicy', function() {

      function restoreOnError() {
        window.uncaughtErrorFilter = window.top.uncaughtErrorFilter = null;
      }

      teardown(function() {
        restoreOnError();
        document.getElementById('target').textContent = '';
      });

      // Errors thrown in custom element reactions are not thrown up
      // the call stack to the dom methods that provoked them, so this
      // wraps Chai's assert.throws to re-throw uncaught errors
      function assertThrows(fn, re) {
        // Catch uncaught errors; note when running in iframe sometimes
        // Safari errors are thrown on the top window, sometimes not, so
        // catch in both places
        let uncaughtError = null;
        window.uncaughtErrorFilter = window.top.uncaughtErrorFilter = function(err) {
          if (!uncaughtError) {
            uncaughtError = err;
          }
          return true;
        };
        assert.throws(function() {
          fn();
          // Re-throw any uncaughtErrors
          if (uncaughtError) {
            throw new Error(uncaughtError.message);
          }
          // Force polyfill reactions and/or async template stamping
          flush();
          // Re-throw any uncaughtErrors
          if (uncaughtError) {
            throw new Error(uncaughtError.message);
          }
        }, re);
        restoreOnError();
      }

      test('dom-bind', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
          '<dom-bind>' +
          '  <template>' +
          '    <div id="injected"></div>'+
          '  </template>`' + 
          '</dom-bind>';
        }, /dom-bind not allowed/);
        assert.notOk(document.getElementById('injected'));
      });

      test('dom-if', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-if if>' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>' + 
            '</dom-if>';
        }, /template owner not trusted/);
        assert.notOk(document.getElementById('injected'));
      });

      test('dom-repeat', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-repeat items="[0]">' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>`' + 
            '</dom-repeat>';
        }, /template owner not trusted/);
        assert.notOk(document.getElementById('injected'));
      });

      test('dom-module never used', function() {
        var el = document.createElement('no-dm');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
      });

      test('dom-module never used (legacy)', function() {
        var el = document.createElement('no-dm-legacy');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
      });

      // From this point down, dom-module lookup is globally enabled
      test('setAllowTemplateFromDomModule', function() {
        setAllowTemplateFromDomModule(true);
      });

      test('dom-module after registration', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-module id="trusted-element">' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>`' +
            '</dom-module>';
        }, /trusted-element re-registered/);
        let el;
        assertThrows(function() {
          el = document.createElement('trusted-element');
          document.getElementById('target').appendChild(el);
        }, /expecting dom-module or null template for trusted-element/);
        assert.notOk(el && el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });
      
      test('dom-module after registration, again', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-module id="trusted-element">' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>`' +
            '</dom-module>';
        }, /trusted-element re-registered/);
        const el = document.createElement('trusted-element');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });
      
      test('dom-module before registration', function() {
        document.getElementById('target').innerHTML =
          '<dom-module id="has-no-template">' +
          '  <template>' +
          '    <div id="injected"></div>'+
          '  </template>`' +
          '</dom-module>';
        class HasNoTemplate extends PolymerElement {
          static get is() { return 'has-no-template'; }
          static get template() { return null; }
        }
        customElements.define(HasNoTemplate.is, HasNoTemplate);
        let el = document.createElement('has-no-template');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });

      test('dom-module after registration (legacy)', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-module id="trusted-element-legacy">' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>`' +
            '</dom-module>';
        }, /trusted-element-legacy re-registered/);
        let el;
        assertThrows(function() {
          el = document.createElement('trusted-element-legacy');
          document.getElementById('target').appendChild(el);
        }, /expecting dom-module or null template for trusted-element-legacy/);
        assert.notOk(el && el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });
      
      test('dom-module after registration, again (legacy)', function() {
        assertThrows(function() {
          document.getElementById('target').innerHTML =
            '<dom-module id="trusted-element-legacy">' +
            '  <template>' +
            '    <div id="injected"></div>'+
            '  </template>`' +
            '</dom-module>';
        }, /trusted-element-legacy re-registered/);
        const el = document.createElement('trusted-element-legacy');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });
            
      test('dom-module before registration (legacy)', function() {
        document.getElementById('target').innerHTML =
          '<dom-module id="has-no-template-legacy">' +
          '  <template>' +
          '    <div id="injected"></div>'+
          '  </template>`' +
          '</dom-module>';
        Polymer({
          is: 'has-no-template-legacy',
          _template: null
        });
        let el = document.createElement('has-no-template-legacy');
        document.getElementById('target').appendChild(el);
        assert.notOk(el.shadowRoot);
        assert.notOk(document.getElementById('injected'));
      });

      test('element without explicit template throws', function() {
        assertThrows(function() {
          class HasNoTemplateThrows extends PolymerElement {
            static get is() { return 'has-no-template-throws'; }
          }
          customElements.define(HasNoTemplateThrows.is, HasNoTemplateThrows); 
          var el = document.createElement('has-no-template-throws');
          document.getElementById('target').appendChild(el);
        }, /expecting dom-module or null template/);
      });

      test('element without explicit template throws (legacy)', function() {
        assertThrows(function() {
          Polymer({
            is: 'has-no-template-throws-legacy'
          });
          var el = document.createElement('has-no-template-throws-legacy');
          document.getElementById('target').appendChild(el);
        }, /expecting dom-module or null template/);
      });

      test('template helpers in trusted templates work', function() {
        
        class TrustedTemplates extends PolymerElement {
          static get template() { return html`
            <dom-repeat items="[0]">
              <template>
                <div id="dom-repeat-ok"></div>
                <dom-if if>
                  <template><div id="nested-dom-if-ok"></div></template>
                </dom-if>
              </template>
            </dom-repeat>
            <dom-if if>
              <template>
                <div id="dom-if-ok"></div>
                <dom-repeat items="[0]">
                  <template>
                    <div id="nested-dom-repeat-ok"></div>
                  </template>
                </dom-repeat>
              </template>
            </dom-if>`; 
          }
        }
        customElements.define('trusted-templates', TrustedTemplates);
    
        var el = document.createElement('trusted-templates');
        document.getElementById('target').appendChild(el);
        flush();
        assert.ok(el.shadowRoot.querySelector('#dom-repeat-ok'));
        assert.ok(el.shadowRoot.querySelector('#dom-if-ok'));
        assert.ok(el.shadowRoot.querySelector('#nested-dom-repeat-ok'));
        assert.ok(el.shadowRoot.querySelector('#nested-dom-if-ok'));
      });

      test('template helpers in trusted templates work (legacy)', function() {

        Polymer({
          is: 'trusted-templates-legacy',
          _template: html`
            <template is="dom-repeat" items="[0]">
              <div id="dom-repeat-ok"></div>
              <template is="dom-if" if><div id="nested-dom-if-ok"></div></template>
            </template>
            <template is="dom-if" if>
              <div id="dom-if-ok"></div>
              <template is="dom-repeat" items="[0]"><div id="nested-dom-repeat-ok"></div></template>
            </template>`
        });

        var el = document.createElement('trusted-templates-legacy');
        document.getElementById('target').appendChild(el);
        flush();
        assert.ok(el.shadowRoot.querySelector('#dom-repeat-ok'));
        assert.ok(el.shadowRoot.querySelector('#dom-if-ok'));
        assert.ok(el.shadowRoot.querySelector('#nested-dom-repeat-ok'));
        assert.ok(el.shadowRoot.querySelector('#nested-dom-if-ok'));
      });

    });

  </script>
    
</body>
</html>